/*******************************************************************************
 * relationalcloud.com
 *  
 *  Project Info:  http://relationalcloud.com
 *  Project Members:  	Carlo Curino <carlo.curino@gmail.com>
 * 				Evan Jones <ej@evanjones.ca>
 *  				Yang Zhang <yaaang@gmail.com> 
 * 				Sam Madden <madden@csail.mit.edu>
 *  This library is free software; you can redistribute it and/or modify it under the terms
 *  of the GNU General Public License as published by the Free Software Foundation;
 *  either version 3.0 of the License, or (at your option) any later version.
 * 
 *  This library is distributed in the hope that it will be useful, but WITHOUT ANY 
 *  WARRANTY;  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
 *  PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 ******************************************************************************/
package com.relationalcloud.workloadanalysis;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;

import com.relationalcloud.misc.YBTerminal;

public class BaloonTester {
  
  static String driver
  ;
  public static void main(String[] args) throws Exception {

    Properties ini = new Properties();
    try {
      ini.load(new FileInputStream(System.getProperty("prop")));
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    driver = ini.getProperty("driver");
    boolean load_baloon = Boolean.parseBoolean(ini.getProperty("load_baloon"));

    
    // Register jdbcDriver
    try {
      Class.forName(driver);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }

      

    Connection conn = DriverManager.getConnection(ini.getProperty("conn"), ini.getProperty("user"), ini
        .getProperty("password"));
    conn.setAutoCommit(false);

    Statement stmt = conn.createStatement();

    // MEASURE INITIAL STATISTICS
    int initMeasureTime = Integer.parseInt(ini.getProperty("initTime")); //seconds
    long oldPR = getStats(stmt,"Innodb_buffer_pool_reads");
   // long oldLR = getStats(stmt,"Innodb_buffer_pool_read_requests");
    Thread.sleep(initMeasureTime*1000); 
    long curPR = getStats(stmt,"Innodb_buffer_pool_reads");
   // long curLR = getStats(stmt,"Innodb_buffer_pool_read_requests");

    double avgPR = (double)((double)curPR-(double)oldPR)/(double)initMeasureTime;
   // long avgLR = (curLR-oldLR)/initMeasureTime;

    System.out.println("#REFERENCE LOGICAL PR: " + avgPR); //+" "+avgLR + " ratio: " +(100*(double)avgPR)/(double)avgLR+"%");

    
    // PREPARE FOR BALOONING
    if(driver.startsWith("com.mysql") && load_baloon){
      stmt.executeUpdate("CREATE TABLE IF NOT EXISTS baloon  ("+
                        "id int,"+
                        "a1 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a2 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a3 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a4 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a5 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a6 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a7 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a8 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a9 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a10 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a11 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a12 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a13 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a14 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a15 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a16 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a17 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a18 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a19 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a20 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a21 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a22 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a23 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a24 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a25 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a26 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a27 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a28 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a29 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a30 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a31 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\","+
                        "a32 char(250) default \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\""+
                    ");");


      stmt.execute("TRUNCATE baloon;"); 
    }
    Random r = new Random();

    int minchunks = 1;
    int maxchunks = 128; //total 2GB
    
    int chunkstep = Integer.parseInt(ini.getProperty("chunkstep"));
      minchunks = Integer.parseInt(ini.getProperty("minchunks"));
      maxchunks = Integer.parseInt(ini.getProperty("maxchunks"));
    int stepTime = Integer.parseInt(ini.getProperty("stepTime"));
      
    // GROW INCREASINGLY 1MB at a time up to maxchunks
    System.out.println("#Baloon-level (MB), Physical Reads (#)");
    System.out.println("0\t" + avgPR);
    
    long tts=0;
    long tte;
    
    for(int i=minchunks;i<=maxchunks; i+=chunkstep){
      String sql="";
      if(load_baloon){
        // INSERT 16 MB WORTH OF DATA FOR MySQL 4MB FOR POSTGRES
        sql="INSERT INTO baloon (id) VALUES";
        for(int u=((i-chunkstep)*2048);u<(((i*2048)-1));u++){
          sql+="("+u+"),";
        }
        sql+="("+(i*2048)+")";
        stmt.execute(sql); 
        stmt.execute("COMMIT"); 
      }
      
      long tstart = System.currentTimeMillis();
      // EVERY 120 SECONDS
      for(int j=0;j<stepTime; j++){
        // EXECUTING SCANS OF OUR TABLES EVERY SECOND (TO KEEP THEM HOT)
        long wait=(1000-(System.currentTimeMillis()-tstart));
        if(wait>0)
          Thread.sleep(wait);
        else
          System.err.println("For j="+j+" took more than 1 seconds to loop!");
        
        //this returns exactly how many pages have been touched for postgres and double the number for MySQL (page size)
        sql="SELECT count(*) FROM (SELECT id FROM baloon WHERE a"+(1+r.nextInt(32))+"!=\'"+r.nextInt(1000)+"\' AND id<"+ (i*2048) +") as tab;";

        //AND MEASURING RESPONSE TIME (IF LOW THEY ARE IN MEMORY)
        tstart = System.currentTimeMillis();
        ResultSet res2 = stmt.executeQuery(sql);
        
        long tend = System.currentTimeMillis();            
        res2.next();
        int pages = res2.getInt(1);
        stmt.execute("COMMIT"); 

        
        if(j==10){
          tts=System.currentTimeMillis();
          oldPR = getStats(stmt,"Innodb_buffer_pool_reads");
        //  oldLR = getStats(stmt,"Innodb_buffer_pool_read_requests");
        }
      }

      tte=System.currentTimeMillis();
      curPR = getStats(stmt,"Innodb_buffer_pool_reads");
     // curLR = getStats(stmt,"Innodb_buffer_pool_read_requests");

      double avgPRpsec = 1000*(double)(curPR-oldPR)/(double)(tte-tts);
      
      if(driver.startsWith("com.mysql")){
         System.out.println(i*16 +"\t"+ avgPRpsec);
      }else{
        System.out.println(i*4 +"\t"+ avgPRpsec);
      }
    
    }   



  }


  public static long getStats(Statement stmt, String t) throws SQLException{



    long results=-1;
    ResultSet res;
    
    if(driver.startsWith("com.mysql")){
      res = stmt.executeQuery("SHOW GLOBAL STATUS WHERE Variable_name=\""+t+"\" ");
      res.next();
      results = res.getLong(2);
    }
    
    if(driver.startsWith("org.postgresql")){
      
      
      String sql= "SELECT sum(blks_read) from pg_stat_database;";
      
      res = stmt.executeQuery(sql);
      res.next();
      results = res.getLong(1);
      stmt.execute("COMMIT");
    }
    
    
    return results;
    
  }

  


}
